﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace FileWalker
{
	/// <summary>
	/// File and directory enumerator.
	/// </summary>
	public sealed class FileWalkEnumerator
	{

		#region Fields

		/// <summary>
		/// Holds start directory name.
		/// </summary>
		private readonly string startFolder_;

		/// <summary>
		/// Holds IFWHandler list.
		/// </summary>
		private List<IFWHandler> fwHandlers_;

		private bool execute_;

		#endregion Fields

		#region Properties
		#endregion Properties

		#region Methods

		/// <summary>
		/// Prevents a default instance of the FileWalkEnumerator class from being created.
		/// </summary>
		private FileWalkEnumerator()
		{
		}

		public void Stop()
		{
			execute_ = false;
		}

		/// <summary>
		/// Initializes a new instance of the Disk class.
		/// </summary>
		/// <param name="startDirectory">Start directory name.</param>
		public FileWalkEnumerator(string startDirectory)
		{
			startFolder_ = startDirectory;
			fwHandlers_ = new List<IFWHandler>();
		}

		/// <summary>
		/// Adds IFWHandler.
		/// </summary>
		/// <param name="handler">IFWHandler to add.</param>
		public void AddHandler(IFWHandler handler)
		{
			fwHandlers_.Add(handler);
		}

		/// <summary>
		/// Performs enumeration.
		/// </summary>
		/// <remarks>
		/// Starts enumeration from the start directory.
		/// </remarks>
		/// <returns>true, if all files and directories was enumerated; otherwise - false.</returns>
		public bool Enumerate()
		{
			bool rslt = false;

			execute_ = true;

			if (0 != fwHandlers_.Count)
			{
				foreach (IFWHandler fwHandler in fwHandlers_)
				{
					fwHandler.FolderChanged(startFolder_, startFolder_);

					if (!execute_)
					{
						return false;
					}
				}

				if (!execute_)
				{
					return false;
				}

				rslt = Enumerate(startFolder_);
			}

			return rslt;
		}

		/// <summary>
		/// Enumerates directory.
		/// </summary>
		/// <remarks>
		/// Enumerates all files in the specified directory.
		/// <para/>The method calls all FileChanged in the handler list for each file in directory.
		/// <para/>Stops enumeration, if at least one handler returns false.
		/// <para/>Enumerates all subdirectories in the specfied directory.
		/// <para/>The method calls all FolderChanged in the handler list for each subdirectory in directory.
		/// <para/>Stops enumeration, if at least one handler returns false.
		/// <para/>The method calls recursively itself for each subdirectory in directory.
		/// </remarks>
		/// <param name="directoryName">Directory to enumearte.</param>
		/// <returns>true, if all files and subdirectories was enumerated; otherwise - false.</returns>
		private bool Enumerate(string directoryName)
		{
			bool is2Continue = execute_;

			if (is2Continue)
			{
				// Check, if it is a link.
				FileAttributes attrs = File.GetAttributes(directoryName);

				if (!((attrs & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint))
				{
					try
					{

						// Get file list.
						foreach (string fileName in Directory.GetFiles(directoryName))
						{
							if (!execute_)
							{
								return false;
							}

							foreach (IFWHandler fwHandler in fwHandlers_)
							{
								if (!execute_)
								{
									return false;
								}

								if (!(is2Continue = fwHandler.FileChanged(startFolder_, fileName)))
								{
									break;
								}
							}

							if (!is2Continue)
							{
								break;
							}
						}
					}

					catch (UnauthorizedAccessException excp)
					{
						System.Windows.MessageBox.Show(String.Format("The caller does not have the required permission.{0}Error: {1}",
										System.Environment.NewLine, excp.Message),
										"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
						is2Continue = false;
					}

					catch (ArgumentException excp)
					{
						System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is not valid.{1}Error: {2}",
										directoryName, System.Environment.NewLine, excp.Message),
										"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
						is2Continue = false;
					}

					catch (PathTooLongException excp)
					{
						System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is too long.{1}Error: {2}",
										directoryName, System.Environment.NewLine, excp.Message),
										"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
						is2Continue = false;
					}

					catch (DirectoryNotFoundException excp)
					{
						System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is not found.{1}Error: {2}",
										directoryName, System.Environment.NewLine, excp.Message),
										"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
						is2Continue = false;
					}

					catch (IOException excp)
					{
						System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is file name.{1}Error: {2}",
										directoryName, System.Environment.NewLine, excp.Message),
										"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
						is2Continue = false;
					}

					if (is2Continue && execute_)
					{
						try
						{
							foreach (string subDirectoryName in Directory.GetDirectories(directoryName))
							{
								if (!execute_)
								{
									return false;
								}

								foreach (IFWHandler fwHandler in fwHandlers_)
								{
									if (!execute_)
									{
										return false;
									}

									if (!(is2Continue = fwHandler.FolderChanged(startFolder_, subDirectoryName)))
									{
										break;
									}
								}

								if (execute_)
								{
									if (is2Continue)
									{
										Enumerate(subDirectoryName);
									}
									else
									{
										is2Continue = true;
									}
								}
								else
								{
									return false;
								}
							}
						}
						catch (UnauthorizedAccessException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The caller does not have the required permission.{0}Error: {1}",
											System.Environment.NewLine, excp.Message),
											"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							is2Continue = false;
						}

						catch (ArgumentException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is not valid.{1}Error: {2}",
											directoryName, System.Environment.NewLine, excp.Message),
											"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							is2Continue = false;
						}

						catch (PathTooLongException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is too long.{1}Error: {2}",
											directoryName, System.Environment.NewLine, excp.Message),
											"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							is2Continue = false;
						}

						catch (DirectoryNotFoundException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is not found.{1}Error: {2}",
											directoryName, System.Environment.NewLine, excp.Message),
											"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							is2Continue = false;
						}

						catch (IOException excp)
						{
							System.Windows.MessageBox.Show(String.Format("The path \"{0}\" is file name.{1}Error: {2}",
											directoryName, System.Environment.NewLine, excp.Message),
											"Folder creation", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
							is2Continue = false;
						}
					}
				}
			}

			if (!execute_)
			{
				return false;
			}

			return is2Continue;
		}
		#endregion Methods
	}
}
